﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Dynamic;
using System.Web.Services.Protocols;
using System.ServiceModel;
using System.Diagnostics;
using System.Threading;
using System.Xml.Linq;

namespace HttpClient
{
    [Serializable]
    public class DynamicHttpException : ApplicationException
    {
        public DynamicHttpException() { }
        public DynamicHttpException(string message) : base(message) { }
        public DynamicHttpException(string message, Exception inner) : base(message, inner) { }
        protected DynamicHttpException(
          System.Runtime.Serialization.SerializationInfo info,
          System.Runtime.Serialization.StreamingContext context)
            : base(info, context) { }
    }
    [Serializable]
    public class SoapBuildException : ApplicationException
    {
        public SoapBuildException() { }
        public SoapBuildException(string message) : base(message) { }
        public SoapBuildException(string message, Exception inner) : base(message, inner) { }
        protected SoapBuildException(
          System.Runtime.Serialization.SerializationInfo info,
          System.Runtime.Serialization.StreamingContext context)
            : base(info, context) { }
    }

    [Serializable]
    public class WcfBuildException : ApplicationException
    {
        public WcfBuildException() { }
        public WcfBuildException(string message) : base(message) { }
        public WcfBuildException(string message, Exception inner) : base(message, inner) { }
        protected WcfBuildException(
          System.Runtime.Serialization.SerializationInfo info,
          System.Runtime.Serialization.StreamingContext context)
            : base(info, context) { }
    }

    [Serializable]
    public class XLinkBuildException : ApplicationException
    {
        public XLinkBuildException() { }
        public XLinkBuildException(string message) : base(message) { }
        public XLinkBuildException(string message, Exception inner) : base(message, inner) { }
        protected XLinkBuildException(
          System.Runtime.Serialization.SerializationInfo info,
          System.Runtime.Serialization.StreamingContext context)
            : base(info, context) { }
    }


    public enum ProxyType
    {
        WebClient,
        SoapClient,
        WcfClient,
        XLinkClient
    } 
    public class DynamicHttpClient : DynamicObject, IDisposable
    {
        #region Clients
        WebClient _webClient = null;
        SoapHttpClientProtocol _soapClient = null;
        ClientBase<IClientChannel> _wcfClient = null;
        XElement _xmlClient;
        WSDLManager wsdlBuilder;

        public Dictionary<ProxyType, Exception> Errors
        {
            get;
            set;
        }

        #endregion

        #region Local
        const int MAX_OBJECT = 10;
        object[] _priorityTable;
        byte[] _downloadedContent;

        public byte[] Content
        {
            get { return _downloadedContent; }
            set { _downloadedContent = value; }
        }

        public void SetPriority(ProxyType ps)
        {
            SetPriority(ps, 0);
        }
        public void SetPriority(ProxyType ps, int pos)
        {
            if (_priorityTable == null)
                throw new DynamicHttpException("Not initialized.");
            object o = _priorityTable[pos];
            switch (ps)
            {
                case ProxyType.SoapClient:
                    if (_soapClient == o)
                        return;
                    else
                        ShiftPriority(pos, _soapClient);
                    break;
                case ProxyType.WcfClient:
                    if (_wcfClient == o)
                        return;
                    else
                        ShiftPriority(pos, _wcfClient);
                    break;
                case ProxyType.WebClient:
                    if (_webClient == o)
                        return;
                    else
                        ShiftPriority(pos, _webClient);
                    break;
                case ProxyType.XLinkClient :
                    if (_xmlClient  == o)
                        return;
                    else
                        ShiftPriority(pos, _xmlClient);
                    break;
         
                default:
                    return;

            }

        }
        private void ShiftPriority(int pos, object temp)
        {
            object o = _priorityTable[pos];
            _priorityTable[pos] = temp;
            //--Shift
            for (int i = pos + 1; i < MAX_OBJECT; i++)
            {
                object nextClient = _priorityTable[i];
                _priorityTable[i] = o;
                o = nextClient;
            }
        }
        #endregion

        #region Variable
        public string Url
        {
            get;
            set;
        }
        #endregion
        /// <summary>
        /// Resource of Web
        /// </summary>
        /// <param name="url"></param>
        public DynamicHttpClient(string url)
        {
            this.Url = url;
            Inititialize();
        }
        private void Inititialize()
        {
            _priorityTable = new object[MAX_OBJECT];
            Errors = new Dictionary<ProxyType, Exception>();
            Build();
        }
        private void Prioritize()
        {
            _priorityTable[0] = _webClient;
            _priorityTable[1] = _soapClient;
            _priorityTable[2] = _wcfClient;
            _priorityTable[3] = _xmlClient;
        }
        protected void Build()
        {
            _webClient = new WebClient();
            AutoResetEvent waiter = new AutoResetEvent(false);
            _webClient.Headers.Add("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)");
            _webClient.DownloadDataCompleted += new DownloadDataCompletedEventHandler(_webClient_DownloadDataCompleted);
            _webClient.DownloadDataAsync( new Uri( Url), waiter);
            waiter.WaitOne();
            bool generated = TryBuildDynamicProxy();
        }
        public virtual bool TryBuildDynamicProxy()
        {
            bool built = true;
            try
            {
                if (_downloadedContent != null && _downloadedContent.Length > 0)
                {

#if CONSOLE
            var hreads = (from h in _webClient.ResponseHeaders.AllKeys.ToList<string>()
                          // where h == "mime"
                          select h);
            foreach (string s in hreads)
            {
                Console.WriteLine( string.Format("Headers Key ={0} Values ={1}", s, _webClient.ResponseHeaders[s]));
            }
#endif
                    string mimeType = _webClient.ResponseHeaders["Content-Type"];
                    if (mimeType.Contains("text") || mimeType.Contains("html") || mimeType.Contains("xml"))
                    {
                        string da = System.Text.UTF8Encoding.Default.GetString(_downloadedContent);
                        try
                        {
                            wsdlBuilder = new WSDLManager(_webClient);
                            _soapClient = wsdlBuilder.GetProxy(Url, "localhost") as SoapHttpClientProtocol;
                        }
                        catch (Exception ef)
                        {
                            Debug.WriteLine("Error creating WSDL proxy.." + ef.Message);
                            Errors.Add(ProxyType.SoapClient,new SoapBuildException("Error creating WSDL proxy", ef));
                        }
                        try
                        {
                            WCFManager wcfBuilder = new WCFManager();
                            _wcfClient = wcfBuilder.GetProxy(da, "") as ClientBase<IClientChannel>;
                        }
                        catch (Exception ik)
                        {
                            Debug.WriteLine("Error creating WCF proxy.." + ik.Message);
                            Errors.Add(ProxyType.WcfClient, new SoapBuildException("Error creating WCF proxy", ik));
                        }
                        try
                        {
                            XLinkManager linkBuilder = new XLinkManager();
                            linkBuilder.GetProxy(da);
                        }
                        catch (Exception ih)
                        {
                            Debug.WriteLine("Error creating XLink proxy.." + ih.Message);
                            Errors.Add(ProxyType.XLinkClient , new SoapBuildException("Error creating XLink proxy", ih));
                        }
                        Prioritize();
                    }
                }
            }
            catch (DynamicHttpException)
            {
                built = false;
            }
            return built;
        }
        void _webClient_DownloadDataCompleted(object sender, DownloadDataCompletedEventArgs e)
        {
            try
            {
                _downloadedContent = e.Result;
            }
            finally
            {
                AutoResetEvent reset = e.UserState as AutoResetEvent;
                reset.Set();
            }
        }
        public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
        {
            bool executed = false;
            bool exTried = false;
            result = null;
            try
            {
                for (int i = 0; i < MAX_OBJECT; i++)
                {
                    object d = _priorityTable[i];
                    if (d == null)
                        continue;
                    else
                    {
                        try
                        {
                            result = d.GetType().InvokeMember(binder.Name, System.Reflection.BindingFlags.InvokeMethod, null, d, args);
                            executed = true;
                            break;
                        }
                        catch (System.MissingMethodException)
                        {
                            continue;
                        }
                        catch (System.Reflection.TargetException)
                        {

                        }
                    }
                }
            }
            catch (Exception)
            {
                exTried = true;
                executed = base.TryInvokeMember(binder, args, out result);
            }
            finally
            {
                if (executed == false && exTried == true )
                {
                    executed = base.TryInvokeMember(binder, args, out result);
                }
            }
            return executed;
        }
        
        public void Dispose()
        {
            if (_webClient != null)
            {
                _webClient.Dispose();
            }
            if (_soapClient != null)
            {
                _soapClient.Dispose();
            }
            if (_wcfClient != null)
            {
                _wcfClient = null;
                //_wcfClient.Dispose();
            }
       
        }

    }
}
